#pragma once
//这个文件主要是来进行基于循环队列的生产者消费者模型的书写
#include<iostream>
#include<unistd.h>
// #include"Cond.hpp" //这个主要是在阻塞队列进行书写的，是为了通过同步条件变量做到生产者和消费者之间的同步（生产者还没有生产的时候，消费者睡眠，生产者消费满了的话，那么就让消费者睡眠，直到用完了才唤醒）
#include"Mutex.hpp"
#include"Sem.hpp"
#include<vector>
//此时对于这个生产者消费者模型，完全可以通过之前写的包装来对这个模型进行书写

//进行多生产者多消费者的书写，此时生产者和生产者，消费者和消费者之间要进行互斥处理，不然就会出现抢票的问题
using namespace MutexMoble;
// using namespace CondMoble;
using namespace SemModule;

const int grac = 5 ; //就设计五个容量

//下面是单生产者，单消费者模型
template<class T>  //定义一个模板类
class  RingQueue
{
public:
    RingQueue(int _grac = grac)
    :_cap(grac),
    _rq(grac),
    _black_mem(_grac), //刚开始生产者（空元素）的个数肯定是满的
    _p_step(0),
    _data_mem(0),
    _v_step(0)
    {}
    
    //生产者进行操作
    void Equeue(const T&in)  //传入的是往循环队列存的内容（如果个信号量中）
    {
        //申请空位置的信号量
        _black_mem.P();  //先预定一下，预定机制要在生产之前
        {
            LockGuard s1(_pmutex);
            //生产
            _rq[_p_step] = in;
            //往后走，位置+1
            ++_p_step;
            //为了防止越界，进行防越界的操作
            _p_step %= _cap;
        }
        //因为已经预定了一块空位置了，所以代表着信号量的位置+1
        _data_mem.V();
    }

    //消费者进行操作
    void Pop(T * out)  //写个输入型变量
    {
        _data_mem.P();
        {
            LockGuard s1(_cmutex);
            *out = _rq[_v_step];
            ++_v_step;
            _v_step %= _cap;
        }
        _black_mem.V();
    }

    ~RingQueue()
    {}  //不用写，成员变量不是内置类型就是自定义类型（自己默认调用自己的析构函数）
private:
    std :: vector<T> _rq;  //通过用数组来替代循环的队列
    int _cap; // 记录可以储存的元素

    //生产者（这里让空位置作为生产者，没生产一个，空位置就少一个）
    Sem _black_mem;
    int _p_step;  //当前的位置

    //消费者
    Sem _data_mem;
    int _v_step;  //当前的位置

    //维护多生产者多消费者的
    Mutex _cmutex;
    Mutex _pmutex;
};